define(function(require, exports, module) {

	var $ = require('$'), Position = require('position'), Shim = require('iframe-shim'), Widget = require('widget');
	
	var doc = (self.frameElement && self.frameElement.tagName == "IFRAME") ? window.parent.document : document;
	
	// Overlay
	// -------
	// Overlay 组件的核心特点是可定位（Positionable）和可层叠（Stackable）
	// 是一切悬浮类 UI 组件的基类

	var Overlay = Widget.extend({

		attrs : {
			// 基本属性
			width : null,
			height : null,
			zIndex : 99,
			visible : false,

			// 定位配置
			align : {
				// element 的定位点，默认为左上角
				selfXY : [ 0, 0 ],
				// 基准定位元素，默认为当前可视区域
				baseElement : Position.VIEWPORT,
				// 基准定位元素的定位点，默认为左上角
				baseXY : [ 0, 0 ]
			},

			// 父元素
			parentNode : document.body
		},

		show : function() {
			// 若从未渲染，则调用 render
			if (!this.rendered) {
				this.render();
			}
			this.set('visible', true);
			return this;
		},

		hide : function() {
			this.set('visible', false);
			return this;
		},

		setup : function() {
			var that = this;
			// 加载 iframe 遮罩层并与 overlay 保持同步
			this._setupShim();
			// 窗口resize时，重新定位浮层
			this._setupResize();
			// 统一在显示之后重新设定位置
			this.after('show', function() {
				that._setPosition();
			});
		},

		destroy : function() {
			// 销毁两个静态数组中的实例
			erase(this, Overlay.allOverlays);
			erase(this, Overlay.blurOverlays);
			return Overlay.superclass.destroy.call(this);
		},

		// 进行定位
		_setPosition : function(align) {
			// 不在文档流中，定位无效
			if (!isInDocument(this.element[0]))
				return;
			align || (align = this.get('align'));

			// 如果align为空，表示不需要使用js对齐
			if (!align)
				return;

			var isHidden = this.element.css('display') === 'none';

			// 在定位时，为避免元素高度不定，先显示出来
			if (isHidden) {
				this.element.css({
					visibility : 'hidden',
					display : 'block'
				});
			}

			Position.pin({
				element : this.element,
				x : align.selfXY[0],
				y : align.selfXY[1]
			}, {
				element : align.baseElement,
				x : align.baseXY[0],
				y : align.baseXY[1]
			});

			// 定位完成后，还原
			if (isHidden) {
				this.element.css({
					visibility : '',
					display : 'none'
				});
			}

			return this;
		},

		// 加载 iframe 遮罩层并与 overlay 保持同步
		_setupShim : function() {
			var shim = new Shim(this.element);

			// 在隐藏和设置位置后，要重新定位
			// 显示后会设置位置，所以不用绑定 shim.sync
			this.after('hide _setPosition', shim.sync, shim);

			// 除了 parentNode 之外的其他属性发生变化时，都触发 shim 同步
			var attrs = [ 'width', 'height' ];
			for ( var attr in attrs) {
				if (attrs.hasOwnProperty(attr)) {
					this.on('change:' + attr, shim.sync, shim);
				}
			}

			// 在销魂自身前要销毁 shim
			this.before('destroy', shim.destroy, shim);
		},

		// resize窗口时重新定位浮层，用这个方法收集所有浮层实例
		_setupResize : function() {
			Overlay.allOverlays.push(this);
		},

		// 除了 element 和 relativeElements，点击 body 后都会隐藏 element
		_blurHide : function(arr) {
			arr = $.makeArray(arr);
			arr.push(this.element);
			this._relativeElements = arr;
			Overlay.blurOverlays.push(this);
		},

		// 用于 set 属性后的界面更新

		_onRenderWidth : function(val) {
			this.element.css('width', val);
		},

		_onRenderHeight : function(val) {
			this.element.css('height', val);
		},

		_onRenderZIndex : function(val) {
			this.element.css('zIndex', val);
		},

		_onRenderAlign : function(val) {
			this._setPosition(val);
		},

		_onRenderVisible : function(val) {
			this.element[val ? 'show' : 'hide']();
		}

	});

	// 绑定 blur 隐藏事件
	Overlay.blurOverlays = [];
	$(document).on('click', function(e) {
		hideBlurOverlays(e);
	});

	// 绑定 resize 重新定位事件
	var timeout;
	var winWidth = $(window).width(), winHeight = $(window).height();
	Overlay.allOverlays = [];
	$(window).resize(function() {
		var winNewWidth = $(window).width(), winNewHeight = $(window).height();
		// IE678 莫名其妙触发 resize
		// http://stackoverflow.com/questions/1852751/window-resize-event-firing-in-internet-explorer
		if (winWidth !== winNewWidth || winHeight !== winNewHeight) {
			timeout && clearTimeout(timeout);
			timeout = setTimeout(function() {
				$(Overlay.allOverlays).each(function(i, item) {
					// 当实例为空或隐藏时，不处理
					if (!item || !item.get('visible')) {
						return;
					}
					item._setPosition();
				});
			}, 80);
		}
		winWidth = winNewWidth;
		winHeight = winNewHeight;
	});

	module.exports = Overlay;

	// Helpers
	// -------

	function isInDocument(element) {
		return $.contains(doc.documentElement, element);
	}

	function hideBlurOverlays(e) {
		$(Overlay.blurOverlays).each(function(index, item) {
			// 当实例为空或隐藏时，不处理
			if (!item || !item.get('visible')) {
				return;
			}

			// 遍历 _relativeElements ，当点击的元素落在这些元素上时，不处理
			for ( var i = 0; i < item._relativeElements.length; i++) {
				var el = $(item._relativeElements[i])[0];
				if (el === e.target || $.contains(el, e.target)) {
					return;
				}
			}

			// 到这里，判断触发了元素的 blur 事件，隐藏元素
			item.hide();
		});
	}

	// 从数组中删除对应元素
	function erase(target, array) {
		for ( var i = 0; i < array.length; i++) {
			if (target === array[i]) {
				array.splice(i, 1);
				return array;
			}
		}
	}

});